package cn.huaqingcheng.tool.crypto.encrypt;

import cn.huaqingcheng.tool.crypto.codec.Base64Tool;

import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA 非对称加密
 *
 * @see <a href="https://developer.aliyun.com/article/901534">浅析五种最常用的Java加密算法 - 阿里云开发者社区</a>
 */
public interface RSATool {

    String RSA = "RSA";

    /**
     * 获取密钥对
     *
     * @return 密钥对
     */
    static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA);
        generator.initialize(1024);
        return generator.generateKeyPair();
    }

    /**
     * RSA加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     */
    static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] doFinal = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));

        return Base64Tool.decodeToStr(doFinal);
    }

    /**
     * RSA解密
     *
     * @param data       待解密数据
     * @param privateKey 私钥
     */
    static String decrypt(String data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] doFinal = cipher.doFinal(Base64Tool.decode(data));

        return new String(doFinal, StandardCharsets.UTF_8);
    }

    static String sign(String raw, String privateKey) throws Exception {
        return sign(raw, getPrivateKey(privateKey));
    }

    /**
     * 获取私钥
     *
     * @param privateKey 私钥字符串
     */
    static PrivateKey getPrivateKey(String privateKey) throws Exception {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Tool.decode(privateKey));
        return KeyFactory.getInstance(RSA).generatePrivate(keySpec);
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     * @return 签名
     */
    static String sign(String data, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(privateKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));

        return Base64Tool.encodeToStr(signature.sign());
    }

    static boolean verify(String raw, String sign, String publicKey) throws Exception {
        return verify(raw, sign, getPublicKey(publicKey));
    }

    /**
     * 获取公钥
     *
     * @param publicKey 公钥字符串
     */
    static PublicKey getPublicKey(String publicKey) throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Tool.decode(publicKey));
        return KeyFactory.getInstance(RSA).generatePublic(keySpec);
    }

    /**
     * 验签
     *
     * @param srcData   原始字符串
     * @param sign      签名
     * @param publicKey 公钥
     * @return 是否验签通过
     */
    static boolean verify(String srcData, String sign, PublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(publicKey);
        signature.update(srcData.getBytes(StandardCharsets.UTF_8));

        return signature.verify(Base64Tool.decode(sign));
    }

}
